/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ajustecurvas.controller;

import ajustecurvas.utils.MetodoGaussSeidel;
import ajustecurvas.Ponto;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Douglas
 */
public class PolinomioTerceiroGrauAjusteController extends AjusteController {

    String equacao;
    double pearson;
    
    @Override
    public List<Ponto> calcular(List<Ponto> medidas) {
        
        double[][] A = new double[4][4];
        double[] Y = new double[4];                
        
        int qtdPontos = medidas.size();
        
        double[] xi = separarEixo(medidas, 0); //vetor de pontos no eixo X
        double[] yi = separarEixo(medidas, 1); //vetor de pontos no eixo Y
        
        A = setMatrizA(A, xi, yi);
        Y = setMatrizY(Y, xi, yi);
        double[] vetXzero = {0,0,0,0};
        
        
        MetodoGaussSeidel solver = new MetodoGaussSeidel();
        ArrayList<double[]> resultados = solver.resolverSistema(A, vetXzero, Y, 10E-5);
        
        gerarEquacao(resultados.get(resultados.size() - 1));
        
        pearson = calcularPearson(xi, yi, resultados.get(resultados.size() - 1));
        
        return getPontos(resultados.get(resultados.size() - 1), qtdPontos, xi);
        
    }

    @Override
    public String getEquacao() {
        return equacao;
    }

    @Override
    public double getQualidade() {
        return pearson;
    }
    
    
    public List<Ponto> getPontos(double[] resultado, int qtdPontos, double[] xi){
        
        List<Ponto> pontos = new ArrayList<Ponto>();
        
        for (int i = 0; i < qtdPontos; i++) {
            pontos.add(new Ponto(xi[i], calcularY(xi[i], resultado[0], resultado[1], resultado[2], resultado[3])));
        }
                
        return pontos;
        
    }
    
    public double calcularY(double x, double a0, double a1, double a2, double a3){
        
        double pontoY = (a0) + (a1*x) + (a2*Math.pow(x, 2)) + (a3*Math.pow(x, 3));
        
        return pontoY;
    }
    
    public double[][] setMatrizA(double[][] A, double[] xi, double[] yi){
        
        //Matriz dos valores de x
		for (int i = 0; i < A.length; i++) {
			int exp = i;
			for (int j = 0; j < A[0].length; j++) {
				A[i][j] = somatorio(1, exp, xi, yi);
				exp++;
			}
		}
        return A;
    }
    
    public double[] setMatrizY(double[] Y, double[] xi, double[] yi){
        
        //Matriz dos valores de y
	for (int i = 0; i < Y.length; i++) {
            Y[i] = somatorio(2, i, xi, yi);
	}
                
        return Y;
        
    }
    
    /**
	 * Realizar os somatorios de Xi^expoente e (Xi^expoente)*Yi
	 * @param tipoSomatorio
	 * @param expoente
	 * @return
	 */
	public double somatorio(int tipoSomatorio, double expoente, double[] xi, double[] yi){
		
		double soma = 0;
		
		switch (tipoSomatorio) {
		case 1:
			for (int i = 0; i < xi.length; i++) { //somatorio que que calcula o valor das posicoes da matriz A
				soma += Math.pow(xi[i], expoente);
			}
			break;
			
		case 2:
			for (int i = 0; i < xi.length; i++) { //somatorio que que calcula o valor das posicoes da matriz Y
				soma += Math.pow(xi[i], expoente) * yi[i];
			}
			break;
			
		default:
			System.out.println("Tipo de somatorio invalido");
			break;
		}
		return soma;
	}
	
 
    private double[] separarEixo(List<Ponto> medidas, int j) {
        double[] valores = new double[medidas.size()];

        for (int i = 0; i < valores.length; i++) {
            if (j == 0) {
                valores[i] = medidas.get(i).getX();
            } else {
                valores[i] = medidas.get(i).getY();
            }
        }

        return valores;
    }
    
    public double calcularPearson(double[] xi, double[] yi, double[] resultado){
        double resultPearson;
        
        double somaYiMenosFdeX = xi.length * somaYiMenosFdeX(yi, xi, resultado); //Somatorio(yi - F(xi))²
        double somaYiElevadaAoQuadrado = Math.pow(soma(yi),2); //(Somatorio(Yi))²
        double somaYiQuadrada = xi.length * somaQuadrada(yi); //Somatorio(Yi²)
        
        double res = somaYiMenosFdeX / (somaYiQuadrada - somaYiElevadaAoQuadrado);
        resultPearson = Math.sqrt(1 - res);
        
        return resultPearson;
    }
    
    public double somaYiMenosFdeX(double[] yi, double[] xi, double[] resultado){
        double soma = 0;
        
        for (int i = 0; i < yi.length; i++) {
            soma += Math.pow(yi[i] - calcularY(xi[i], resultado[0], resultado[1], resultado[2], resultado[3]), 2);
        }
        
        return soma;
    }
    
    public double somaQuadrada(double[] yi){
        double soma = 0;
        
        for (int i = 0; i < yi.length; i++) {
            soma += Math.pow(yi[i], 2);
        }
        return soma;
    }
    
    public double soma(double[] yi){
        double soma = 0;
        
        for (int i = 0; i < yi.length; i++) {
            soma += yi[i];
        }
        
        return soma;
    }
    
    public void gerarEquacao(double[] resultados) {
        DecimalFormat df = new DecimalFormat("0.00");  
        String a0 = df.format(resultados[0]);
        String a1 = df.format(resultados[1]);
        String a2 = df.format(resultados[2]);
        String a3 = df.format(resultados[3]);
        equacao = "" + a0 + " + (" + a1 + "*x )" + " + (" + a2 + "x ^ 2) + (" + a3 + "x ^ 3)";
    }
    
}
